"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.exit = exports.retrieveCols = exports.NewAction = void 0;
const chalk_1 = require("chalk");
const inquirer = require("inquirer");
const path_1 = require("path");
const dasherize_1 = require("../lib/utils/dasherize");
const questions_1 = require("../lib/questions/questions");
const git_runner_1 = require("../lib/runners/git.runner");
const schematics_1 = require("../lib/schematics");
const ui_1 = require("../lib/ui");
const abstract_action_1 = require("./abstract.action");
const child_process_1 = require("child_process");
const semver = require('semver');
class NewAction extends abstract_action_1.AbstractAction {
    handle(inputs, options) {
        return __awaiter(this, void 0, void 0, function* () {
            const dryRunOption = options.find((option) => option.name === 'dry-run');
            const isDryRunEnabled = dryRunOption && dryRunOption.value;
            yield askForMissingInformation(inputs, options);
            yield generateApplicationFiles(inputs, options).catch(exports.exit);
            const shouldSkipInstall = options.some((option) => option.name === 'skip-install' && option.value === true);
            const shouldSkipGit = options.some((option) => option.name === 'skip-git' && option.value === true);
            const projectDirectory = dasherize_1.dasherize(getApplicationNameInput(inputs).value);
            if (!shouldSkipInstall) {
                yield installPackages(options, isDryRunEnabled, projectDirectory);
            }
            if (!isDryRunEnabled) {
                if (!shouldSkipGit) {
                    yield initializeGitRepository(projectDirectory);
                    // await createGitIgnoreFile(projectDirectory);
                }
                printCollective();
            }
            process.exit(0);
        });
    }
}
exports.NewAction = NewAction;
const getApplicationNameInput = (inputs) => inputs.find((input) => input.name === 'name');
const getCpaasPackagesOption = (inputs) => inputs.find((input) => input.name === 'cpaasPackages');
const getIsPure = (options) => !!options.find((input) => input.name === 'pure').value;
const getCpaasFrontPackagesOption = (inputs) => inputs.find((input) => input.name === 'cpaasFrontVersion');
const askForMissingInformation = (inputs, options) => __awaiter(void 0, void 0, void 0, function* () {
    console.info(ui_1.MESSAGES.PROJECT_INFORMATION_START);
    console.info();
    const prompt = inquirer.createPromptModule();
    const nameInput = getApplicationNameInput(inputs);
    if (!nameInput.value) {
        const message = 'What name would you like to use for the new project?';
        const questions = [questions_1.generateInput('name', message)('cpaas-app')];
        const answers = yield prompt(questions);
        replaceInputMissingInformation(inputs, answers);
    }
    const isPure = getIsPure(options);
    const nonInteractive = options.find((option) => option.name === 'nonInteractive').value;
    const registry = options.find((option) => option.name === 'registry')
        .value;
    if (nonInteractive) {
        return;
    }
    if (!isPure) {
        const cpaasFrontVersion = getCpaasFrontPackagesOption(options);
        if (!cpaasFrontVersion.value) {
            try {
                const cpaasFrontVersionList = yield getPackageVersions('cpaas-front', registry);
                const message = '选择 cpaas-front 版本:';
                const defaultOption = cpaasFrontVersionList[0];
                const questions = [
                    questions_1.generateSelectWithDefault('cpaasFrontVersion')(message)(cpaasFrontVersionList)(defaultOption),
                ];
                const answers = yield prompt(questions);
                cpaasFrontVersion.value = answers['cpaasFrontVersion'];
            }
            catch (e) {
                console.error(e);
            }
        }
        const packagesOptions = getCpaasPackagesOption(options);
        if (!packagesOptions.value) {
            const message = '选择额外的 Cpaas 模块?';
            const cpaasModules = getCpaasModules();
            const checkboxOptions = cpaasModules.map((item) => item.desc ? `${item.name} - [${item.desc}]` : item.name);
            const defaultOptions = cpaasModules
                .filter((item) => item.defaultInstall)
                .map((item) => item.desc ? `${item.name} - [${item.desc}]` : item.name);
            const questions = [
                questions_1.generateCheckbox('cpaasPackages')(message)(checkboxOptions)(defaultOptions),
            ];
            const answers = yield prompt(questions);
            const packages = answers['cpaasPackages'];
            const resultPackages = [];
            const registry = options.find((option) => option.name === 'registry')
                .value;
            for (let pInfo of packages) {
                const packageName = pInfo.replace(/ -.*$/, '');
                try {
                    const versionList = yield getPackageVersions(packageName, registry, cpaasFrontVersion === null || cpaasFrontVersion === void 0 ? void 0 : cpaasFrontVersion.value);
                    const message = `选择 ${pInfo} 版本:`;
                    const defaultOption = versionList[0];
                    const questions = [
                        questions_1.generateSelectWithDefault('v')(message)(versionList)(defaultOption),
                    ];
                    const answers = yield prompt(questions);
                    let v = answers['v'];
                    if (v) {
                        resultPackages.push(`${packageName}@${v}`);
                    }
                }
                catch (e) {
                    console.error(e);
                }
            }
            packagesOptions.value = resultPackages.join(',');
        }
    }
});
const getPackageVersions = (packageName, registry, maxVersion) => __awaiter(void 0, void 0, void 0, function* () {
    return new Promise((resolve, reject) => {
        child_process_1.exec(`npm view ${packageName} versions --json --registry=${registry}`, {}, (error, stdout) => {
            if (error) {
                return reject(error);
            }
            let allResult = [];
            try {
                allResult = Array.from(JSON.parse(stdout)).reverse();
            }
            catch (e2) {
                console.log(`Error: npm view ${packageName} versions --json --registry=${registry}\n`);
                return reject(stdout);
            }
            let result = allResult;
            if (maxVersion) {
                result = allResult.filter((v) => {
                    return semver.lte(v, maxVersion.replace(/\.[^/.]+$/, '.999'));
                });
                if (result.length === 0) {
                    result = allResult;
                }
            }
            else {
                result = allResult;
            }
            resolve(result);
        });
    });
});
const getCpaasModules = () => {
    const allCpaasPackageConfig = require('cpaas-webpack-scripts/config/cpaas-packages.js');
    if (allCpaasPackageConfig.packages) {
        return allCpaasPackageConfig.packages;
    }
    return [];
};
const replaceInputMissingInformation = (inputs, answers) => {
    return inputs.map((input) => (input.value =
        input.value !== undefined ? input.value : answers[input.name]));
};
const generateApplicationFiles = (args, options) => __awaiter(void 0, void 0, void 0, function* () {
    const collectionName = options.find((option) => option.name === 'collection' && option.value != null).value;
    const collection = schematics_1.CollectionFactory.create(collectionName || schematics_1.Collection.CPAAS);
    const schematicOptions = mapSchematicOptions(args.concat(options));
    yield collection.execute('cpaas-front-parent', schematicOptions);
    console.info();
});
const mapSchematicOptions = (options) => {
    return options.reduce((schematicOptions, option) => {
        if (option.name !== 'skip-install' &&
            option.value !== 'package-manager') {
            schematicOptions.push(new schematics_1.SchematicOption(option.name, option.value));
        }
        return schematicOptions;
    }, []);
};
const installPackages = (options, dryRunMode, installDirectory) => __awaiter(void 0, void 0, void 0, function* () {
    if (dryRunMode) {
        console.info();
        console.info(chalk_1.default.green(ui_1.MESSAGES.DRY_RUN_MODE));
        console.info();
        return;
    }
    try {
        yield lernaBootsrap(installDirectory, options);
        console.info();
        console.info(ui_1.MESSAGES.PACKAGE_MANAGER_INSTALLATION_SUCCEED(installDirectory));
        console.info(ui_1.MESSAGES.GET_STARTED_INFORMATION);
        console.info();
        console.info(chalk_1.default.gray(ui_1.MESSAGES.CHANGE_DIR_COMMAND(installDirectory)));
        console.info(chalk_1.default.gray(ui_1.MESSAGES.START_COMMAND('yarn')));
        console.info();
    }
    catch (error) {
        console.error(error);
        console.info();
        console.info('First Require Install:');
        console.info(chalk_1.default.gray(ui_1.MESSAGES.CHANGE_DIR_COMMAND(installDirectory)));
        console.info(chalk_1.default.gray(`$ yarn install`));
        console.info();
        // process.exit(1);
    }
});
const lernaBootsrap = (installDirectory, options) => __awaiter(void 0, void 0, void 0, function* () {
    return new Promise((resolve, reject) => {
        var _a, _b;
        const registry = options.find((option) => option.name === 'registry')
            .value;
        let lernaBin = 'lerna';
        try {
            lernaBin = `node \"${require.resolve('lerna/cli')}\"`;
        }
        catch (e) {
            console.log(e);
        }
        const p = child_process_1.exec(`${lernaBin} bootstrap --registry=${registry}`, {
            cwd: installDirectory,
            windowsHide: true,
        }, (error, stdout) => {
            if (error) {
                return reject(error);
            }
            resolve(stdout);
        });
        (_a = p.stdout) === null || _a === void 0 ? void 0 : _a.pipe(process.stdout);
        (_b = p.stderr) === null || _b === void 0 ? void 0 : _b.pipe(process.stderr);
        if (p.stdin) {
            process.stdin.pipe(p.stdin);
        }
    });
});
const initializeGitRepository = (dir) => __awaiter(void 0, void 0, void 0, function* () {
    const runner = new git_runner_1.GitRunner();
    yield runner.run('init', true, path_1.join(process.cwd(), dir)).catch(() => {
        console.error(chalk_1.default.red(ui_1.MESSAGES.GIT_INITIALIZATION_ERROR));
    });
});
const printCollective = () => {
    const dim = print('dim');
    const yellow = print('yellow');
    const emptyLine = print();
    emptyLine();
    yellow(`Thanks for installing Cpaas ${ui_1.EMOJIS.PRAY}`);
    dim('Please consider donating to our open collective');
    dim('to help us maintain this package.');
    emptyLine();
    emptyLine();
    // print()(
    //   `${chalk.bold(`${EMOJIS.WINE}  Donate:`)} ${chalk.underline(
    //     'https://opencollective.com/nest',
    //   )}`,
    // );
    emptyLine();
};
const print = (color = null) => (str = '') => {
    const terminalCols = exports.retrieveCols();
    const strLength = str.replace(/\u001b\[[0-9]{2}m/g, '').length;
    const leftPaddingLength = Math.floor((terminalCols - strLength) / 2);
    const leftPadding = ' '.repeat(Math.max(leftPaddingLength, 0));
    if (color) {
        str = chalk_1.default[color](str);
    }
    console.log(leftPadding, str);
};
exports.retrieveCols = () => {
    const defaultCols = 80;
    try {
        const terminalCols = child_process_1.execSync('tput cols', {
            stdio: ['pipe', 'pipe', 'ignore'],
        });
        return parseInt(terminalCols.toString(), 10) || defaultCols;
    }
    catch (_a) {
        return defaultCols;
    }
};
exports.exit = (...args) => {
    console.log(...args);
    process.exit(1);
};
